package com.luych.toolbox.docker.service.sdk.image;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.luych.toolbox.docker.service.sdk.image.model.DockerImageDeleteResult;
import com.luych.toolbox.docker.service.sdk.image.model.DockerImageHistoryResult;
import com.luych.toolbox.docker.service.sdk.image.model.DockerImageInspectResult;
import com.luych.toolbox.docker.service.sdk.image.model.DockerImagesPushResult;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

public class DockerImage {

    private final String url;
    private final String id;

    public DockerImage(String url, String id) {
        this.url = url;
        this.id = id;
    }

    public DockerImageInspectResult inspect() {
        final String uri = url + "/images/{id}/json";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(uri, DockerImageInspectResult.class, id);
    }

    public DockerImageHistoryResult history() {
        final String uri = url + "/images/{id}/history";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(uri, DockerImageHistoryResult.class, id);
    }

    public List<DockerImagesPushResult> push(String username, String password) {
        return this.push(username, password, null);
    }

    public List<DockerImagesPushResult> push(String username, String password, Consumer<DockerImagesPushResult> consumer) {
        final String uri = url + "/images/{id}/push";
        Map<String, Object> userPass = new HashMap<String, Object>(){{
            this.put("username", username);
            this.put("password", password);
        }};
        String userPassString;
        try {
            userPassString = new ObjectMapper().writeValueAsString(userPass);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Convert registryConfig to json error", e);
        }
        userPassString = Base64Utils.encodeToString(userPassString.getBytes(StandardCharsets.UTF_8));
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.set("X-Registry-Auth", userPassString);
        HttpEntity<?> httpEntity = new HttpEntity<>(null, httpHeaders);
        RestTemplate restTemplate = new RestTemplate();
        RequestCallback requestCallback = restTemplate.httpEntityCallback(httpEntity);
        ResponseExtractor<List<DockerImagesPushResult>> responseExtractor = response -> {
            InputStream inputStream = response.getBody();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line;
            List<DockerImagesPushResult> pushResults = new ArrayList<>();
            while ((line = bufferedReader.readLine()) != null) {
                DockerImagesPushResult pushResult = new ObjectMapper().readValue(line, DockerImagesPushResult.class);
                if (consumer != null) {
                    consumer.accept(pushResult);
                }
                pushResults.add(pushResult);
            }
            return pushResults;
        };
        return restTemplate.execute(uri, HttpMethod.POST, requestCallback, responseExtractor, id);
    }

    public void tag(String name, String tag) {
        final String uri = url + "/images/{id}/tag?repo={1}&tag={2}";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, Object.class, id, name, tag);
    }

    public DockerImageDeleteResult delete() {
        final String uri = url + "/images/{id}?force=true";
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<DockerImageDeleteResult> responseEntity = restTemplate.exchange(uri, HttpMethod.DELETE, null, DockerImageDeleteResult.class, id);
        return responseEntity.getBody();
    }

    public <T> T get(ResponseExtractor<T> responseExtractor) {
        final String uri = url + "/images/{id}/get";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.execute(uri, HttpMethod.GET, request -> {}, responseExtractor, id);
    }

    public void get(File file) {
        this.get(file, null);
    }

    public void get(File file, Consumer<Long> downloaded) {
        ResponseExtractor<?> responseExtractor = response -> {
            InputStream inputStream = response.getBody();
            FileOutputStream outputStream = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            int length;
            long total = 0;
            while ((length = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, length);
                total += length;
                if (downloaded != null) {
                    downloaded.accept(total);
                }
            }
            outputStream.flush();
            outputStream.close();
            return Object.class;
        };
        this.get(responseExtractor);
    }
}
